package server;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Hashtable;

import com.musicg.fingerprint.FingerprintManager;
import com.musicg.fingerprint.FingerprintSimilarity;
import com.musicg.fingerprint.FingerprintSimilarityComputer;
import com.musicg.wave.Wave;

public class Server_Thread implements Runnable {
	private Thread _t; // Thread Client
	private Socket _s; // Client socket
	private PrintWriter _out; // output writer
	private BufferedReader _in; // input buffer
	private Server _server; // Have a pointer on Server in order to use method
	private int _numClient=0; // id client
	Hashtable files = new Hashtable();
	public static String fileName;

	
	/**
	 * Constructor
	 * @param s
	 * @param server
	 */
	Server_Thread(Socket s, Server server) {
		_server=server; 
	    _s=s; 
	    
	    try {
	      _out = new PrintWriter(_s.getOutputStream()); // Output for string
	      _in = new BufferedReader(new InputStreamReader(_s.getInputStream())); // Input for string
	      _numClient = server.addClient(_out); // Save number of client and attach flux
	    } catch (IOException e) {
	    	System.out.println("[ERROR] : "+e);
	    }

	    _t = new Thread(this); // instanciation du thread
	    _t.start(); // Thread RUN ! 
	 }

	
	 static public void sendInChunks(DataInputStream input, DataOutputStream output, int togo, int chunksize) throws IOException {
			
			byte bytes[] = new byte[chunksize];
			int writecount;
			while (togo > 0) {
			    writecount = (togo >= chunksize ? chunksize : togo);
			    input.read(bytes, 0, writecount);
			    output.write(bytes, 0,  writecount);
			    togo = togo - writecount;
			}
			
			output.flush();
	 }
	 
	 
	 static private void receiveFile(Hashtable files, DataInputStream input, DataOutputStream output) {
			String ack=null;             
				
			try {
			    String filename = input.readUTF();
			    System.out.println("Receiving file " + filename);
			    fileName = filename;
			    output.writeUTF("ACK");

			    // get filesize
			    int togo = input.readInt();         

			    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
			    DataOutputStream bytestream = new DataOutputStream(bytes);

			    // transfer file contents
			    sendInChunks(input, bytestream, togo, 1024);
			    bytestream.flush();

			    // store file contents
			    files.put(filename, bytes.toByteArray());
			    OutputStream ot = new FileOutputStream(filename);
			    ot.write(bytes.toByteArray());
			    ot.close();
			    // acknowledge storage of file
			    output.writeUTF("ACK");
			    System.out.println("File stored.");
			} catch (Exception e) {
			    System.out.println("Server failed to store the file.");
			}
	 }
	
	  
	 public void run() {
		 String message = ""; // Client message
		 String name = "";
		 String path_reference = "";
		 String path_test_file = "";
		 int bits;
		 
		 System.out.println("New connection user ID : "+_numClient);
		 
		 try {
			DataInputStream dIn = new DataInputStream(_s.getInputStream());
			DataOutputStream dOut = new DataOutputStream(_s.getOutputStream());
			boolean done = false;
			while(!done) {
			  byte messageType = dIn.readByte();
			  
			  /**
			   * Protocol is : 
			   * 	- 1 : Create an account
			   * 	- 2 : Login a user
			   * 	- 3 : Disconnect
			   * 
			   * Ack is :
			   * 	- 1 : OK
			   * 	- -1 : not OK
			   */
			  switch(messageType)
			  {
			  case 1: // Creating an account
				  System.out.println("-- User "+_numClient+" : Creating an account");
				  name = dIn.readUTF();
				  System.out.println("- "+_numClient+" Read name : "+ name);
				  receiveFile(files, dIn, dOut);
				  System.out.println("- "+_numClient+" : Creating account "+name+" - "+fileName);
				  server.Person p = new server.Person(name, fileName);
				  _server.getDatabase().add(p);
				  _server.write_database();
				  System.out.println("- "+_numClient+" : Account created "+name+" - "+fileName);
				  dOut.writeUTF("1");
				  done=true;
			    break;
			  case 2: // Login a user
				  System.out.println("-- User "+_numClient+" : Wants to login");
				  name = dIn.readUTF();
				  System.out.println("- "+_numClient+" Read name : "+ name);
				  receiveFile(files,dIn, dOut);
				  System.out.println("- "+_numClient+" Read audio compare : "+ fileName);
				  System.out.println("- "+_numClient+" : Analysing signal...");
				  
				  // reseach user
				  int find = -1;
				  for(int i=0; i<_server.getDatabase().size(); i++) {
					  //System.out.println(_server.getDatabase().elementAt(i).getName());
					  if(_server.getDatabase().elementAt(i).getName().trim().equals(name.trim())) {
						  find = i;
					  }
				  }
				  
				  if(find != -1) {
					// Analyse signal  
				  	String official = "official_"+name.trim()+".wav";
				  	String nofficial = fileName;
				  	Wave wave_official = new Wave(official);
			        Wave wave_nofficial = new Wave(nofficial);
			        FingerprintManager fm = new FingerprintManager();
					FingerprintSimilarity fs = new FingerprintSimilarity();
					
					byte[] fingerprint1=new byte[0];
					byte[] fingerprint2=new byte[0];
					System.out.println("Fingerprints");
					
					fingerprint1 = fm.extractFingerprint(wave_official);
					System.out.println("fp 1 : "+fingerprint1);
					
					fingerprint2 = fm.extractFingerprint(wave_nofficial);
					System.out.println("fp 2 : "+fingerprint2);
					
					FingerprintSimilarityComputer fsc = new FingerprintSimilarityComputer(fingerprint1, fingerprint2);
					fsc.getFingerprintsSimilarity().getSimilarity();
			        FingerprintSimilarity similarity;
			        
			        similarity = wave_official.getFingerprintSimilarity(wave_nofficial);
			        System.out.println("Similarity : " + similarity.getSimilarity());
			        
			        if(similarity.getSimilarity() >= 0.5) {
			        	System.out.println("- "+_numClient+" : Login win with similarity "+ similarity.getSimilarity());
			        	dOut.writeUTF("1");
			        } else {
			        	System.out.println("- "+_numClient+" : Login failed with similarity "+ similarity.getSimilarity());
			        	dOut.writeUTF("-1");
			        }
			      // End Analyse signal
				  System.out.println("- "+_numClient+" : End log "+name+" - "+fileName);
				  } else {
					  System.out.println("- "+_numClient+" : No account found for this name");
					  dOut.writeUTF("-1");
				  }
				  done=true;
			    break;
			  case 3: // Disconnect 
				  System.out.println("-- User "+_numClient+" : End of communication");
				  //_server.delClient(_numClient);
				  done = true;
			    break;
			  }
			}
			dIn.close();
			System.out.println("End connection user ID : "+_numClient);
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	  }
}